package x.ovo.wechat.bot.common.http.request;

import lombok.Getter;
import lombok.experimental.Accessors;
import x.ovo.wechat.bot.common.Constant;
import x.ovo.wechat.bot.common.http.HttpEngine;
import x.ovo.wechat.bot.common.http.session.LoginSession;
import x.ovo.wechat.bot.common.util.TypeUtil;

import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * API 请求的基类，封装了请求的参数和响应的结果。
 * 该类为抽象类，用于定义通用的API请求行为和属性。
 *
 * @param <T> 响应类型，继承自ApiResponse。
 * @author ovo
 * @since 2024/05/04
 */
@Getter
//@Setter
@Accessors(chain = true)
public abstract class ApiRequest<T> {

    protected static final String GET = "GET";
    protected static final String POST = "POST";
    // 会话对象，用于存储登录状态和相关用户信息。
    protected final LoginSession session;
    // 基础请求对象，包含所有请求的共通信息。
    protected final BaseRequest baseRequest;
    // 请求超时时间，默认30秒
    protected int timeout = 30000;
    // 是否以JSON格式发送请求，默认为false
    protected boolean json = false;
    // 是否支持多部分文件上传，默认为false
    protected boolean multipart = false;
    // 是否禁止重定向，默认为false
    protected boolean noRedirect = false;
    // 请求的URL
    protected String url;
    // 上传文件时的文件名
    protected String filename;
    // 请求方法，默认为"GET"
    protected String method;
    // 请求内容类型，默认为"application/x-www-form-urlencoded"
    protected String contentType = "application/x-www-form-urlencoded; charset=UTF-8";
    // 请求头信息
    protected Map<String, String> headers = new HashMap<>();
    // 请求参数
    protected Map<String, Object> parameters = new HashMap<>();

    /**
     * 构造函数：创建一个ApiRequest实例。
     *
     * @param url     请求的URL地址。
     * @param session 用户的登录会话信息，用于权限验证等。
     */
    protected ApiRequest(String url, LoginSession session) {
        this(session);
        this.url = url;
    }

    /**
     * 构造函数用于创建一个ApiRequest实例。
     *
     * @param session LoginSession对象，代表一个登录会话。它提供了基础请求对象和其他会话相关信息。
     *                该参数用于初始化ApiRequest的内部状态，包括基础请求和会话特定的请求头。
     */
    public ApiRequest(LoginSession session) {
        this.session = session;
        this.method = GET;
        this.baseRequest = session.getBaseRequest();
        // 初始化默认请求头
        // 这里设置了若干通用的请求头信息，包括接受类型、用户代理、连接方式、接受语言和接受编码。
        this.headers.put("Accept", "*/*");
        this.headers.put("User-Agent", Constant.USER_AGENT);
        this.headers.put("Connection", "close");
        this.headers.put("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
//        this.headers.put("Accept-Encoding", "gzip, deflate, br, zstd");
        this.headers.put("Content-Type", this.contentType);
        this.headers.put("Client-Version", "2.0.0");
        this.headers.put("extspam", Constant.EXTSPAM);
        this.headers.put("Dnt", "1");
        try {
            URL url1 = new URL(session.getUrl());
            this.headers.put("Host", url1.getHost());
            this.headers.put("Origin", url1.getProtocol() + "://" + url1.getHost());
            this.headers.put("Referer", url1.getProtocol() + "://" + url1.getHost() + "/?target=t");
        } catch (Exception ignore) {
        }
    }

    /**
     * 处理响应的函数模板。
     * 该方法是一个抽象方法，需要在具体实现类中被具体化。它的目的是为了处理某种类型的响应，并返回一个泛型T的结果。
     *
     * @return Function<String, T> 返回一个函数，该函数接受一个字符串类型的输入，并返回泛型T类型的输出。这个设计允许延迟处理或者链式处理响应数据。
     */
    public abstract Function<String, T> responseHandler();

    /**
     * 添加请求头。
     *
     * @param key   头部字段的键
     * @param value 头部字段的值
     * @return 返回当前请求对象，以便于链式调用
     */
    public ApiRequest<T> addHeader(String key, String value) {
        this.headers.put(key, value);
        return this;
    }

    /**
     * 添加请求参数。
     *
     * @param key   参数的键
     * @param value 参数的值
     * @return 返回当前请求对象，以便于链式调用
     */
    public ApiRequest<T> addParameter(String key, Object value) {
        this.parameters.put(key, value);
        return this;
    }

    /**
     * 获取响应类型。利用反射获取当前对象的泛型参数类型。
     *
     * @return 响应类型的实例
     */
    @SuppressWarnings("unchecked")
    public Class<T> getResponseType() {
        return (Class<T>) TypeUtil.getTypeArgument(this.getClass());
    }

    /**
     * 执行API请求，返回响应结果。
     *
     * @param engine HTTP引擎，用于发送请求
     * @return API响应结果
     */
    public T execute(HttpEngine<?> engine) {
        if (this.isJson()) {
            this.contentType = "application/json; charset=UTF-8";
            this.addHeader("Content-Type", this.contentType);
        }
        return engine.execute(this, this.responseHandler());
    }

}
